﻿namespace DomainServices.Tools.SharedTypes
{
    using DomainServices.Tools;
    using DomainServices.Tools.SourceLocation;
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;

    internal class SharedCodeService : ISharedCodeService, IDisposable
    {
        private ConcurrentDictionary<CodeMemberKey, SharedCodeDescription> _cachedDescriptions = new ConcurrentDictionary<CodeMemberKey, SharedCodeDescription>();
        private FilenameMap _filenameMap = new FilenameMap();
        private SourceFileLocationService _locationService;
        private DomainServices.Tools.SharedTypes.SharedAssemblies _sharedAssemblies;
        private DomainServices.Tools.SharedTypes.SharedSourceFiles _sharedSourceFiles;

        internal SharedCodeService(SharedCodeServiceParameters parameters, ILoggingService loggingService)
        {
            string symbolSearchPath = (parameters.SymbolSearchPaths == null) ? null : string.Join(";", parameters.SymbolSearchPaths);
            ISourceFileProviderFactory[] providerFactories = new ISourceFileProviderFactory[] { new SourceInfoSourceFileProviderFactory(), new PdbSourceFileProviderFactory(symbolSearchPath, loggingService) };
            this._locationService = new SourceFileLocationService(providerFactories, this._filenameMap);
            this._sharedSourceFiles = new DomainServices.Tools.SharedTypes.SharedSourceFiles(this._locationService, this._filenameMap, parameters.SharedSourceFiles);
            this._sharedAssemblies = new DomainServices.Tools.SharedTypes.SharedAssemblies(parameters.ClientAssemblies, parameters.ClientAssemblyPathsNormalized, loggingService);
        }

        public void Dispose()
        {
            if (this._locationService != null)
            {
                this._locationService.Dispose();
                this._locationService = null;
            }
            this._filenameMap = null;
        }

        public CodeMemberShareKind GetMethodShareKind(string typeName, string methodName, IEnumerable<string> parameterTypeNames)
        {
            CodeMemberKey key = CodeMemberKey.CreateMethodKey(typeName, methodName, (parameterTypeNames == null) ? null : parameterTypeNames.ToArray<string>());
            return this.GetSharedCodeDescription(key).ShareKind;
        }

        public CodeMemberShareKind GetPropertyShareKind(string typeName, string propertyName)
        {
            CodeMemberKey key = CodeMemberKey.CreatePropertyKey(typeName, propertyName);
            return this.GetSharedCodeDescription(key).ShareKind;
        }

        internal SharedCodeDescription GetSharedCodeDescription(CodeMemberKey key)
        {
            return this._cachedDescriptions.GetOrAdd(key, delegate (CodeMemberKey k) {
                string fileName = this.SharedAssemblies.GetSharedAssemblyPath(key);
                if (fileName != null)
                {
                    return new SharedCodeDescription(CodeMemberShareKind.SharedByReference, new int[] { this._filenameMap.AddOrGet(fileName) });
                }
                int[] sharedFileIds = this.SharedSourceFiles.GetSharedFileIds(key);
                if ((sharedFileIds != null) && (sharedFileIds.Length != 0))
                {
                    return new SharedCodeDescription(CodeMemberShareKind.SharedBySource, sharedFileIds);
                }
                return new SharedCodeDescription(CodeMemberShareKind.NotShared, null);
            });
        }

        public CodeMemberShareKind GetTypeShareKind(string typeName)
        {
            CodeMemberKey key = CodeMemberKey.CreateTypeKey(typeName);
            return this.GetSharedCodeDescription(key).ShareKind;
        }

        private DomainServices.Tools.SharedTypes.SharedAssemblies SharedAssemblies
        {
            get
            {
                return this._sharedAssemblies;
            }
        }

        private DomainServices.Tools.SharedTypes.SharedSourceFiles SharedSourceFiles
        {
            get
            {
                return this._sharedSourceFiles;
            }
        }

        internal class SharedCodeDescription
        {
            private int[] _sharedFileIds;
            private CodeMemberShareKind _shareKind;

            public SharedCodeDescription(CodeMemberShareKind shareKind, int[] sharedFileIds)
            {
                this._shareKind = shareKind;
                this._sharedFileIds = sharedFileIds;
            }

            internal int[] SharedFileIds
            {
                get
                {
                    return this._sharedFileIds;
                }
            }

            internal CodeMemberShareKind ShareKind
            {
                get
                {
                    return this._shareKind;
                }
            }
        }
    }
}

